package org.totoro.amap.service;

import com.googlecode.javaewah.datastructure.BitSet;
import org.totoro.dto.MultipleTag;
import org.totoro.enums.Strategy;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;

/**
 * @author daocr
 * @date 2019-04-29
 */
public abstract class AbstractBitmap {


    /**
     * 根据名字 获取 bitmap
     *
     * @param tagName
     * @return
     */
    public abstract BitSet getBitmapByTagName(String tagName);


    protected BitSet find(List<MultipleTag> multipleTag, List<Long> ids) {

        // 按照策略分组
        Map<Strategy, List<MultipleTag>> group = multipleTag.stream().collect(Collectors.groupingBy(MultipleTag::getStrategy));
        // 排序
        List<Strategy> sort = group.keySet().stream().sorted(Comparator.comparing(Strategy::sort)).collect(Collectors.toList());

        BitMapContainer bitMapContainer = new BitMapContainer(ids);

        // 开始匹配数据
        sort.forEach(sortIndex -> {

            List<MultipleTag> multipleTags = group.get(sortIndex);

            BitSet result = multipleTags.stream().collect(new Collector<MultipleTag, BitMapContainer, BitSet>() {

                @Override
                public Supplier<BitMapContainer> supplier() {
                    return BitMapContainer::new;
                }

                @Override
                public BiConsumer<BitMapContainer, MultipleTag> accumulator() {
                    return (bitMapContainer, multipleTag) -> {

                        BitSet tagBitSet = getBitmapByTagName(multipleTag.getTagName());

                        BitSet result = multipleTag.getStrategy().apply(bitMapContainer.getBitSet(), Optional.ofNullable(tagBitSet).orElse(new BitSet()));

                        bitMapContainer.setBitSet(result);
                    };
                }

                @Override
                public BinaryOperator<BitMapContainer> combiner() {
                    throw new RuntimeException("不支持并行调用");
                }

                @Override
                public Function<BitMapContainer, BitSet> finisher() {
                    return BitMapContainer::getBitSet;
                }

                @Override
                public Set<Characteristics> characteristics() {
                    return Collections.emptySet();
                }
            });

            bitMapContainer.setBitSet(result);
        });

        return bitMapContainer.getBitSet();
    }

    /**
     * bit 容器
     */
    public static class BitMapContainer {


        private BitSet bitSet = new BitSet();

        public BitMapContainer() {

        }

        public BitMapContainer(List<Long> ids) {
            ids.forEach(e -> bitSet.set(e.intValue()));
        }

        public BitSet getBitSet() {
            return bitSet;
        }

        public void setBitSet(BitSet bitSet) {
            this.bitSet = bitSet;
        }
    }
}
